using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

using Syncfusion.Windows.Forms.Diagram;
using Syncfusion.Windows.Forms.Diagram.Controls;

using KLATencor.CommonApp.Infrastructure.StateMachine;

using StateMachine_Graphical_Editor.Utils;
using StateMachine_Graphical_Editor.Shapes;
using StateMachine_Graphical_Editor.Forms;

namespace StateMachine_Graphical_Editor.Graphics_Rendering
{
    /// <summary>
    /// Implementation of initializer used by the SubStatesForm
    /// </summary>
    public class SubStateInitializer : Initializer
    {
        #region Members:

        private StatesForm owner;
        private State rootState;
        private GState initialState;
        private GInitial initialElement;

        private GraphValidator graphValidator;

        private PaletteGroupView palette;
        private NodeCollection GSubStates;
        private NodeCollection GTransitions;
        private NodeCollection GSubStateElements; // all elements of the diagram

        private NodeCollection rootStateGlobalElements;

        #endregion

        #region Ctor:

        public SubStateInitializer(PaletteGroupView palette, State root)
        {
            this.rootState = root;
            this.graphValidator = new GraphValidator(rootState.Substates);
            this.palette = palette;

            this.GSubStates = new NodeCollection();
            this.GTransitions = new NodeCollection();
            this.GSubStateElements = new NodeCollection();
        }

        #endregion

        #region Initializer Implementation:

        public NodeCollection getGraphicalStateList()
        {
            //only option to get in here is that the root state is new and has no substate
            if (!hasSubStates(rootState))
            {
                long nextId = owner.getNextID();
                rootState.Substates.AddState(
                    new State(
                        rootState.StateMachine, "SubState" + nextId, nextId, rootState));
                rootState.InitialSubstateName = "SubState" + nextId;
            }

            //create graphical states
            foreach (State substate in rootState.Substates)
            {
                GState state = new GState(palette, substate, this.owner);
                GSubStates.Add(state);
            }

            //connect the states
            foreach (GState subState in GSubStates)
            {
                foreach (Transition trans in subState.LogicState.Transitions)
                {
                    if (graphValidator.unnecessaryOutGoingTransition(trans))
                        continue;
                    //check if multiple transitions:
                    GTransition duplicated = graphValidator.checkMultipleTransitions(trans, GTransitions);
                    if (duplicated != null)
                    {
                        duplicated.addName(trans.Name);
                        duplicated.MultipleTransitions = true;
                        continue;
                    }
                    //else
                    Node target = findEndPointState(trans.Target.Name);

                    if (target != null)
                    {
                        connectNodes(subState, target, trans.Name, trans);
                    }
                }
            }

            //deal with in going transitions
            foreach (GState state in GSubStates)
            {
                IList<Transition> inGoingTransitions = InGoingTransitionDS.getDS().getInGoingTransitions(state.LogicState);
                if (inGoingTransitions == null || inGoingTransitions.Count == 0)
                    continue;
                //else:
                foreach (Transition transition in inGoingTransitions)
                {
                    if (graphValidator.unnecessaryInGoingTransition(transition))
                        continue;
                    //check if multiple transitions
                    GTransition duplicated = graphValidator.checkMultipleTransitions(transition, GTransitions);
                    if (duplicated != null)
                    {
                        duplicated.addName(transition.Name);
                        duplicated.MultipleTransitions = true;
                        continue;
                    }

                    //else
                    Node source = findEndPointState(transition.Source.Name);

                    if (source != null)
                    {
                        connectNodes(source, state, transition.Name, transition);
                    }

                }
            }

            //deal with initial
            setInitialState();
            //deal with final state
            setFinalState();
            // insert all transitions and states to singular collection
            GSubStateElements.AddRange(GSubStates);
            GSubStateElements.AddRange(GTransitions);

            //add the transitions going out from the root state
            rootStateGlobalElements = getRootGlobalGraphicElements();

            GSubStateElements.AddRange(rootStateGlobalElements);

            return GSubStateElements;
        }

        public StateCollection getLevelStates()
        {
            return rootState.Substates;
        }

        public Object getRoot()
        {
            return rootState;
        }

        public GState getInitial()
        {
            return initialState;
        }

        public void setOwner(StatesForm owner)
        {
            this.owner = owner;
        }

        public GTransition connectNodes(Node src, Node dst, String name, Transition currTrans)
        {
            GTransition connector = null;
            
            //add Xml requierd data
            if (currTrans != null)
            {
                connector = new GTransition(PointF.Empty, new PointF(1, 1), currTrans, this.owner);
                connector.TransitionName = currTrans.Name;
                //check for cross hierardhy transition
                if (src.Name != currTrans.Source.Name ||
                    dst.Name != currTrans.Target.Name ||
                    src is OuterState || dst is OuterState)
                    connector.CrossHierarchy = true;
            }
            else
            {
                connector = new GTransition(PointF.Empty, new PointF(1, 1), this.owner);
                connector.Id = -5;
                connector.AbbrevName = "";
                connector.SourceState = src.Name;
                connector.TargetState = dst.Name;
                connector.TransitionName = name;
                connector.EventName = "none";
            }

            //insert connector to the displayed items
            GTransitions.Add(connector);

            src.CentralPort.TryConnect(connector.TailEndPoint);
            dst.CentralPort.TryConnect(connector.HeadEndPoint);

            return connector;
        }

        public GState getGraphicalState(State logical)
        {
            foreach (Node node in GSubStates)
            {
                GState tmp = node as GState;
                if (tmp != null &&
                    tmp.LogicState != null &&
                    tmp.LogicState.Equals(logical))
                {
                    return tmp;
                }
            }
            return null; //doesn't supposed to get here
        }

        public GTransition getGraphicalTransition(Transition logical)
        {
            Node nSource = findEndPointState(logical.Source.Name);
            Node nTarget = findEndPointState(logical.Target.Name);

            foreach (GTransition gTrans in GTransitions)
            {
                if (gTrans.FromNode.Equals(nSource) && gTrans.ToNode.Equals(nTarget))
                    return gTrans;
            }
            return null;
        }

        public void switchInitial(GState newInitial)
        {
            //if not changed - do nothing
            if (newInitial.Equals(initialState) || !newInitial.IsInitialState)
                return;

            LineConnector connector = null;
            foreach (Node node in initialElement.EdgesLeaving)
            {
                connector = node as LineConnector;
            }
            if (connector != null)
            {
                GState oldInitial = connector.ToNode as GState;
                if (oldInitial != null)
                    oldInitial.IsInitialState = false;

                initialState = newInitial;

                newInitial.CentralPort.TryConnect(connector.HeadEndPoint);

                //move to proper position
                initialElement.PinPoint = new PointF(newInitial.PinPoint.X, newInitial.PinPoint.Y - 80);
            }

        }

        public GTransition checkMultipleTransitions(Transition transition)
        {
            return graphValidator.checkMultipleTransitions(transition, GTransitions);
        }

        #endregion

        #region Private Methods:

        /// <summary>
        /// Sets the initial state element, and connects it with the initial sub state
        /// </summary>
        private void setInitialState()
        {
            initialState = GSubStates.FindNodeByName(rootState.InitialSubstateName) as GState;
            if (initialState!=null)
            {
                //set the initial state flag
                initialState.IsInitialState = true;

                initialElement = new GInitial(palette);
                //TODO: maybe in GSubStateElements???
                GSubStates.Add(initialElement);

                //if has transition from the parent to initial substate:
                Transition initialTrans = getInitialTransition();
                string name = (initialTrans == null) ? "" : initialTrans.Name;

                connectNodes(initialElement, initialState, name, initialTrans);
            }
        }

        /// <summary>
        /// Sets the final state element, and connects it with all final sub states 
        /// </summary>
        private void setFinalState()
        {
            GFinal centralFinal = new GFinal(palette);
            
            foreach (Node temp in GSubStates)
            {
                GState state = temp as GState;
                if (state != null)
                {
                    if (state.EdgesLeaving.Count == 0)
                    {
                        state.IsFinalState = true;
                        connectNodes(state, centralFinal, "", null);
                    }
                }
            }
            
            if (centralFinal.EdgesEntering.Count > 0)
                //TODO: maybe in GSubStateElements???
                GSubStates.Add(centralFinal);
            //else
            return;
        }

        /// <summary>
        /// Retrieves the graphical representation of a given state's name
        /// </summary>
        /// <param name="endPointName">The name to find a representing graphical element</param>
        /// <returns>The graphical representation of the given name</returns>
        private Node findEndPointState(String endPointName)
        {
            //Check if the desired GState is the main State of the form:
            if (rootStateGlobalElements!=null && rootStateGlobalElements.FindNodeByName(endPointName) != null)
                return initialElement;

            Node node = GSubStates.FindNodeByName(endPointName);
            //endpoint is not in this hierarchy level:
            if (node == null)
            {
                //check if an OuterState already was entered to ElementCollection
                node = GSubStateElements.FindNodeByName(endPointName);
                if (node == null)
                {
                    StateCollection subCollection = rootState.Substates;
                    foreach (State state in subCollection)
                    {
                        State tmp = state.Substates.Find(endPointName);
                        //state is the endpoint's ancestor
                        if (tmp != null)
                        {
                            node = GSubStates.FindNodeByName(state.Name);
                            return node;
                        }
                    }
                    //endpoint is in higher hierarchy
                    node = new OuterState(endPointName, rootState.StateMachine, owner);
                    //add the node
                    GSubStateElements.Add(node);
                }
            }
            return node;
        }

        /// <summary>
        /// Returns a transition between the main state of the form to its initial sub state
        /// </summary>
        /// <returns>The transition between the parent state and its initial sub state, or
        /// <para>null if no specific transition is defined</para></returns>
        private Transition getInitialTransition()
        {
            Transition result = null;
            foreach (Transition tmp in rootState.Transitions)
            {
                if (tmp.Source.Equals(rootState) && tmp.Target.Name.Equals(rootState.InitialSubstateName))
                {
                    result = tmp;
                    break;
                }
            }
            return result;
        }

        /// <summary>
        /// Creates graphic representation for all the global transitions and the root state of the owner form
        /// </summary>
        /// <returns>A collection of the graphic transitions and root state</returns>
        private NodeCollection getRootGlobalGraphicElements()
        {
            NodeCollection collection = new NodeCollection();
            GState gRoot = new GState(palette, rootState, this.owner);
            gRoot.Visible = false;
            collection.Add(gRoot);

            foreach (Transition trans in rootState.Transitions)
            {
                GTransition global = new GTransition(PointF.Empty, new PointF(1, 1), trans, this.owner);
                global.Visible = false;
                collection.Add(global);
            }
            return collection;
        }

        /// <summary>
        /// checks if state has substates or not
        /// </summary>
        /// <param name="parent"></param>
        /// <returns>true if state has substates</returns>
        private bool hasSubStates(State parent)
        {
            int statesCount = 0;
            foreach (State state in parent.Substates)
            {
                statesCount++;
            }
            return statesCount > 0;
        }

        //TODO: when using a KTOS.CommonApp.Infrastructure.StateMachine.dll with the usage of
        //      Parent property, replace this method with the property!!!
        /// <summary>
        /// determines if 2 states are children of same parent
        /// </summary>
        /// <param name="state1"></param>
        /// <param name="state2"></param>
        /// <returns>true if the states are siblings, false otherwise</returns>
        private bool isSiblings(State state1, State state2)
        {
            if (state1.Parent.Equals(state2.Parent))
                return true;
            return false;
        }

        #endregion

    }
}
